from PyQt5.QtWidgets import QWidget, QGraphicsPixmapItem, QGraphicsView, QGraphicsScene
from PyQt5.QtCore import Qt, QRectF, QSize
from PyQt5.QtGui import QIcon, QImage, QPainter, QResizeEvent, QPixmap, QContextMenuEvent

from GUI.Widget.ImageWindow_design import Ui_ImageWindow
from GUI.Widget.Layer.ImageLayerList import ImageLayerList
from GUI.Widget.Canvas import GraphicsPixmapItem
from GUI.Widget.ControllerPanel import ControlledPanel


# 图层混合模式名字
CompositionModeNameList = [
    {
        'id': QPainter.CompositionMode_SourceOver,
        'name': '正常'
    },
    {
        'id': QPainter.CompositionMode_Multiply,
        'name': '正片叠底'
    },
    {
        'id': QPainter.CompositionMode_ColorDodge,
        'name': '颜色减淡'
    },
    {
        'id': QPainter.CompositionMode_Darken,
        'name': '变暗'
    },
    {
        'id': QPainter.CompositionMode_ColorBurn,
        'name': '颜色加深'
    },
    {
        'id': QPainter.CompositionMode_Lighten,
        'name': '变亮'
    },
    {
        'id': QPainter.CompositionMode_Overlay,
        'name': 'Overlay'
    },
    {
        'id': QPainter.CompositionMode_HardLight,
        'name': '强光'
    },
    {
        'id': QPainter.CompositionMode_SoftLight,
        'name': '柔光'
    },
    {
        'id': QPainter.CompositionMode_Plus,
        'name': '实色混合'
    },
    {
        'id': QPainter.CompositionMode_Screen,
        'name': '减去'
    },
    {
        'id': QPainter.CompositionMode_Difference,
        'name': '差值'
    },
    {
        'id': QPainter.CompositionMode_Exclusion,
        'name': '排除'
    },
    {
        'id': QPainter.CompositionMode_Clear,
        'name': '清除'
    },
    {
        'id': QPainter.CompositionMode_Xor,
        'name': '异或'
    },

]


class ImageWindow(QWidget, Ui_ImageWindow):
    # 数据
    canvasSize: QSize = None  # 画布大小
    image: QImage = None  # 画布数据
    trackMouseMove = False  # 不允许 自定义鼠标移动事件
    title = ''  # 标题
    # 部件
    window = None  # 主窗体
    listItem = None  # 列表项目
    imageLayer: ImageLayerList = None
    view: QGraphicsView = None  # 视图
    scene: QGraphicsScene = None # 场景
    controllerPanel = None  # 控制器面板

    def __init__(self, window):
        self.window = window
        super().__init__(window.imagePanel)
        self.__initUI(window.imagePanel)
        self.hide()

    def __initUI(self, parent):
        self.setupUi(self)
        self.__initPanel()
        # 定位
        self.setGeometry(0, 0, parent.width(), parent.height())

    def __initPanel(self):
        # 图层列表
        layer = self.imageLayer
        layer.itemRowChanged.connect(self.layerRowChangedSlot)

        # 图像视图
        view = self.view

        # 场景
        scene = QGraphicsScene()  # 创建场景
        view.setScene(scene)
        scene.setBackgroundBrush(Qt.transparent)
        self.scene = scene

        # 控制器器面板
        controllerPanel = ControlledPanel(self)
        controllerPanel.setWindowFlag(Qt.FramelessWindowHint)
        controllerPanel.setGeometry(0, 0, self.geometry().width(), self.geometry().height())
        self.controllerPanel = controllerPanel

        # 图层混合模式
        nameList = CompositionModeNameList
        comBox = self.comboBox_layerMenu_mode
        comBox.flag = True  # 为True时,响应当前列表改变事件
        for i in range(len(nameList)):
            comBox.addItem(nameList[i]['name'])
        comBox.currentIndexChanged.connect(self.__layerCompositionModeChangeSlot)

        # 图层透明度
        spinBox = self.spinBox_layerMenu_alpha
        spinBox.flag = True  # 为True时,响应当前值改变事件
        spinBox.valueChanged.connect(self.__layerAlphaChangeSlot)

    @property
    def currentLayer(self):
        return self.imageLayer.currentItem()

    def resizeEvent(self, event: QResizeEvent):
        # 让控制器面板跟随父对象变化
        self.controllerPanel.setGeometry(0, 0, self.geometry().width(), self.geometry().height())
        # 视图
        self.view.updatePos()
        # 图层列表
        #pos = self.layerPanel.geometry()
        #self.imageLayer.setGeometry(0, 0, pos.width(), pos.height())

    def saveCanvas(self):
        size = self.canvasSize  # 画布大小
        scene = self.scene
        image = QImage(size.width(), size.height(), QImage.Format_ARGB32)
        rect = scene.sceneRect()
        # rect = QRectF(view.mapFromScene(rect.topLeft()), view.mapFromScene(rect.bottomRight()))
        qp = QPainter()
        qp.begin(image)
        scene.render(qp)
        qp.end()
        self.image = image

    def setCanvasSize(self, size: QSize):
        # 储存画布大小
        self.canvasSize = size
        self.view.updatePos()
        # 更改视图大小并居中
        # 更改场景大小
        scene = self.scene
        rect = QRectF(0, 0, size.width(), size.height())
        scene.setSceneRect(rect)

    def addPixmapLayer(self, image: QImage, select=True):
        """
        :param image: QImage
        :param select: bool
        :return: 图层句柄
        """
        view = self.view
        scene = self.scene
        pixmap = QPixmap.fromImage(image)
        item = GraphicsPixmapItem(pixmap)  # 创建像素图元
        item.setEnabled(False)
        newLayer = self.imageLayer.newLayer({
            'image': image,
            'graphicsItem': item,
            'layerType': 'pixmap',
            'icon': QIcon(pixmap),
        })
        item.listItem = newLayer
        if select:
            self.imageLayer.setSelectedLayer(newLayer)
        scene.addItem(item)
        return newLayer

    def forItemsInScene(self, filter=None):
        """
        遍历场景中所有item并通过filter函数过滤，返回一个列表
        :param filter: 过滤函数,它应该返回一个boolean值
        :return: List of QGraphicsSceneItem
        """
        scene = self.scene
        items = scene.items()
        if filter:
            ret = []
            for item in items:
                if filter(item):
                    ret.append(item)
            return ret
        else:
            items

    def forItemAtPoint(self, pos, filter=None):
        """
        返回事件鼠标点下第一个 visible and enable 的GraphicsItem
        :param pos: QPointF #GraphicsItem.mapToScene(event.pos())
        :param filter: filter(QGraphicsItem)
        :return: list of QGraphicsItem
        """
        items = self.scene.items(pos)
        if filter:
            ret = []
            for each in items:
                if filter(each):
                    ret.append(each)
            return ret
        else:
            return items

    def resetGraphicsItemZIndex(self):
        """
        根据图层顺序，重置图层的GraphicsItem 在场景中的Z-index
        :return:
        """
        scene = self.scene
        imageLayer = self.imageLayer
        items = imageLayer.allItems
        length = len(items)
        if length > 0:
            for i in range(0, length):
                graphicsItem = items[i].graphicsItem
                graphicsItem.setZValue(length - i - 1)
                print(items[i].text(), '变更到Z：', length - i - 1)

    def layerRowChangedSlot(self):
        """
        图层顺序改变事件
        :return:
        """
        self.resetGraphicsItemZIndex()

    def addTransform(self):
        controllerPanel = self.controllerPanel
        controllerPanel.addTransform()

    def __layerCompositionModeChangeSlot(self):
        if self.comboBox_layerMenu_mode.flag:
            index = self.comboBox_layerMenu_mode.currentIndex()
            nameList = CompositionModeNameList
            mode = nameList[index]['id']
            layer = self.currentLayer
            if layer:
                layer.compositionMode = index
                layer.graphicsItem.compositionMode = mode
                layer.graphicsItem.update()

    def __layerAlphaChangeSlot(self):
        if self.spinBox_layerMenu_alpha.flag:
            alpha = self.spinBox_layerMenu_alpha.value()
            layer = self.currentLayer
            if layer:
                layer.alpha = alpha
                layer.graphicsItem.setOpacity(alpha/100)

    def updateLayerCompositionMode(self, layer):
        comboBox = self.comboBox_layerMenu_mode
        comboBox.flag = False
        comboBox.setCurrentIndex(layer.compositionMode)
        comboBox.flag = True

    def updateLayerAlpha(self, layer):
        spinBox = self.spinBox_layerMenu_alpha
        spinBox.flag = False
        spinBox.setValue(layer.alpha)
        spinBox.flag = True
